import { Comment } from '@babel/types';
import { isNotEmptyObject, randomCode } from '../utils';

import { IMethod } from '../types/vue-sfc';
import { splitCommentBlock } from './index';
import { IParam, IReturn, IOption } from '../types/normal';

/**
 * 注解标记
 */
export const marks = {
    /**
     * 兼容 Vuese 标记
     * 用于 Vue Event Emit 中标记回调参数
     */
    /**
     * @arg {类型|类型} 说明
     */
    arg: '@arg',

    /**
     * @param {类型|类型} 参数名 说明
     */
    param: '@param',

    /**
     * @option value 说明
     */
    option: '@option',

    /**
     * @return {类型|类型} 说明
     */
    return: '@return',
    /**
     * @returns {类型|类型} 说明
     */
    returns: '@returns',

    /**
     * @description 说明
     */
    description: '@description',

    /**
     * Vue 组件实例可调用函数
     *
     * 一个块注释里面只允许定义一个 public 注解
     */
    /**
     * @public 说明
     * @param {类型|类型} 参数名 说明
     */
    public: '@public',

    /**
     * Vue event emit 注解，允许定义多个
     *
     * update:xxx 类型的事件不会出现在文档中，而是用于判断 prop 是否是 .sync 和 v-model
     */
    /**
     * @event change
     * @description 说明
     * @param {类型|类型} 参数名 说明
     *
     * @event update:value
     * @description 说明
     * @param {类型|类型} 参数名 说明
     */
    event: '@event'
};

export function isCommentBlock(comment: Comment) {
    return comment.type === 'CommentBlock';
}

export function isPublicMark(comment: Comment | string) {
    let value = '';
    if (typeof comment === 'string') {
        value = comment;
    } else {
        value = comment.value;
    }
    const isPub = new RegExp(marks.public);
    return !!value.match(isPub);
}

export function isEventMark(comment: Comment | string) {
    let value = '';
    if (typeof comment === 'string') {
        value = comment;
    } else {
        value = comment.value;
    }
    const isEve = new RegExp(marks.event);
    return !!value.match(isEve);
}

export function isPureContent(content: string) {
    return Object.values(marks).every(e => !content.startsWith(e));
}

/**
 * 解析 public 注解
 *
 * 只要是打了该注解，都会返回信息
 *
 * 支持多个块注释和多个行注释的写法
 */
export function parseCommentForPublicMark(comment: Comment | readonly Comment[]) {
    const parser = (commentStr: string) => {
        const info: IMethod = {};
        for (let str of splitCommentBlock(commentStr)) {
            if (str.startsWith(marks.public)) {
                const desp = str.split(/\s+/)[1] || '';
                if (desp) info.desp = desp;
                // ...
            } else if (str.startsWith(marks.param)) {
                const param = parseCommentForParamMark(str);
                if (param) info.params = (info.params || []).concat([param]);
                // ...
            } else if (str.startsWith(marks.return) || str.startsWith(marks.returns)) {
                const ret = parseCommentForReturnsMark(str);
                if (ret) info.returns = ret;
            }
        }
        return info;
    };

    let commentStr = '';
    if (comment instanceof Array) {
        comment.forEach(t => {
            commentStr += t.value.trim() + '\n';
        });
    } else {
        commentStr = comment.value;
    }

    if (isPublicMark(commentStr)) return parser(commentStr);
}

/**
 * 解析 event 注解
 *
 * 一段块注释允许定义多个 Event 信息
 */
export function parseCommentForEventMark(comment: Comment) {
    const infos: Array<{
        name?: string;
        desp?: string;
        params?: Array<IParam>;
    }> = [];

    if (isCommentBlock(comment) && isEventMark(comment)) {
        let info: typeof infos[0] = {};

        for (let str of splitCommentBlock(comment.value)) {
            if (str.startsWith(marks.event)) {
                if (isNotEmptyObject(info)) infos.push(info);
                const name = str.split(/\s+/)[1].trim();
                if (name) info = { name };
                // ...
            } else if (info && str.startsWith(marks.description)) {
                const desp = str.split(/\s+/)[1].trim();
                if (desp) info.desp = desp;
                // ...
            } else if (info && str.startsWith(marks.param)) {
                const param = parseCommentForParamMark(str);
                if (param) info.params = (info.params || []).concat([param]);
            }
        }

        // 插入最后一个数据
        if (isNotEmptyObject(info)) infos.push(info);
    }

    return infos;
}

/**
 * 解析 event 注解，返回详细信息
 *
 * 一段块注释允许定义多个 Event 信息
 */
export function parseCommentForEventMarkDetails(code: string, comment: Comment) {
    const infos: Array<{
        name?: string;
        desp?: string;
        params?: Array<IParam>;
        start: number;
        end: number;
    }> = [];

    const commentStart = comment.start || 0;
    const commentEnd = comment.end || 0;
    let commentValue = code.substring(commentStart, commentEnd);

    const events = parseCommentForEventMark(comment);
    events.forEach(event => {
        const startKey = randomCode(marks.event.length);
        commentValue = commentValue.replace(marks.event, startKey);

        // 搜寻对应 Event 结尾的位置
        let endKey = '';
        let endLineIdx = null;
        let startComputed = false;
        let commentLines = commentValue.split('\n');
        commentLines = commentLines.map((line, idx) => {
            if (line.match(startKey)) {
                startComputed = true;
            } else if (line.trim() === '*' && startComputed) {
                endLineIdx = idx - 1;
                startComputed = false;
            }
            return line;
        });

        if (endLineIdx !== null) {
            endKey = randomCode(commentLines[endLineIdx].length);
            commentLines[endLineIdx] = endKey;

            commentValue = commentLines.join('\n');

            const startIndex = commentValue.indexOf(startKey);
            const endIndex = commentValue.indexOf(endKey) + endKey.length - 1; // -1 是 \r 字符

            infos.push({
                ...event,
                start: startIndex + commentStart,
                end: endIndex + commentStart
            });
        }
    });

    return infos;
}

/**
 * 解析 param 注解
 */
export function parseCommentForParamMark(str: string) {
    const info: IParam = {};
    let items = str.split(/\s/);

    if (items.length > 1) {
        // 有类型
        if (items[1] && items[1].trim().match(/^{/)) {
            const matchStr = new RegExp('{.*}').exec(str)?.[0];
            if (matchStr) {
                const type = matchStr.replace(/{|}/g, '');
                info.type = type.split('|').map(s => s.trim());
                if (info.type.includes('*')) info.type = ['any'];
                if (!info.type.length) delete info.type;

                str = str.replace(/.*}/, '').trim();
                items = str.split(/\s+/);
                const name = items[0].trim();
                if (name) info.name = name;

                items = items.slice(1);
                const desp = items.join(' ').trim().replace(/^-?/, '');
                if (desp) info.desp = desp;
            }

            // ...
        } else {
            const name = items[1].trim();
            if (name) info.name = name;
            items = items.slice(2);
            const desp = items.join(' ').trim().replace(/^-?/, '');
            if (desp) info.desp = desp;
        }
    }

    if (isNotEmptyObject(info)) {
        return info;
    } else {
        return null;
    }
}

/**
 * 解析 arg 注解
 */
export function parseCommentForArgMark(str: string) {
    const info: IParam = {};
    let items = str.split(/\s/);

    if (items.length > 1) {
        // 有类型
        if (items[1] && items[1].trim().match(/^{/)) {
            const matchStr = new RegExp('{.*}').exec(str)?.[0];
            if (matchStr) {
                const type = matchStr.replace(/{|}/g, '');
                info.type = type.split('|').map(s => s.trim());
                if (info.type.includes('*')) info.type = ['any'];
                if (!info.type.length) delete info.type;
                str = str.replace(/.*}/, '').trim();
                if (str) info.desp = str;
            }
        } else {
            items = items.slice(1);
            const desp = items.join(' ').trim().replace(/^-?/, '');
            if (desp) info.desp = desp;
        }
    }

    if (isNotEmptyObject(info)) {
        return info;
    } else {
        return null;
    }
}

/**
 * 解析 option 注解
 */
export function parseCommentForOptionMark(str: string) {
    let items = str.split(/\s/);
    if (items.length) {
        const info: IOption = {};
        const name = items[1].trim();
        if (name) info.name = name;
        items = items.slice(2);
        const desp = items.join(' ').trim().replace(/^-?/, '');
        if (desp) info.desp = desp;
        return isNotEmptyObject(info) ? info : undefined;
    }
}

/**
 * 解析 return / returns 注解
 */
export function parseCommentForReturnsMark(str: string) {
    const info: IReturn = {};
    let items = str.split(/\s/);

    if (items.length) {
        // 有类型
        if (items[1] && items[1].trim().match(/^{/)) {
            const matchStr = new RegExp('{.*}').exec(str)?.[0];
            if (matchStr) {
                const type = matchStr.replace(/{|}/g, '').trim();
                info.type = type.split('|').map(s => s.trim());
                if (info.type.includes('*')) info.type = ['any'];
                if (!info.type.length) delete info.type;

                str = str.replace(/.*}/, '').trim();
                items = str.split(/\s+/);
                const desp = items.join(' ').trim().replace(/^-?/, '');
                if (desp) info.desp = desp;
            }

            // ...
        } else {
            items = items.slice(1);
            const desp = items.join(' ').trim().replace(/^-?/, '');
            if (desp) info.desp = desp;
        }
    }

    if (isNotEmptyObject(info)) {
        return info;
    } else {
        return null;
    }
}
